/* Copyright (c) 2010, Iain Wade <iwade@optusnet.com.au>
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef __SC101_H__
#define __SC101_H__

#include <sys/types.h>
#include <sys/mutex.h>
#include <sys/queue.h>
#include <netinet/in.h>

struct mbuf;
struct outstanding;
struct outstanding_head;
struct outstanding_io;
struct outstanding_io_head;
struct outstanding_io_split;
struct device_state;

struct sc101_attach_req {
  char id[64];
  in_addr_t broadcast;
};

#define SC101_ATTACH _IOW(0xDD, 0x00, struct sc101_attach_req)

typedef enum {
  DIR_IN = 0,  // in to the host (i.e. read).
  DIR_OUT = 1,  // out to the device (i.e. write).
} io_dir_t;

typedef void (*packet_handler_cb)(struct sockaddr_in *addr, struct mbuf *m,
                                  size_t len, struct outstanding *out);
typedef void (*timeout_handler_cb)(struct outstanding *out);

struct outstanding {
  uint8_t cmd;
  int16_t seq;
  uint16_t len;

  packet_handler_cb packet_handler;
  timeout_handler_cb timeout_handler;
  void *ctx;

  int timeout_ms;
  int timeout;  // ticks

  TAILQ_ENTRY(outstanding) entries;
};

TAILQ_HEAD(outstanding_head, outstanding);

typedef void (*io_complete_cb)(struct device_state *dev, int error, void *ctx);

struct outstanding_io {
  struct device_state *dev;
  struct sockaddr_in *addr;

  io_dir_t dir;
  off_t offset;
  long bcount;
  caddr_t data;

  io_complete_cb cb;
  void *ctx;

  u_int attempt;
  u_int timeout_ms;
  struct outstanding outstanding;
  STAILQ_ENTRY(outstanding_io) entries;
};

STAILQ_HEAD(outstanding_io_head, outstanding_io);

struct outstanding_io_split {
  struct mtx lock;
  struct outstanding_io *io;
  int error;
  int pending;
};

struct device_state {
  struct mtx lock;

  char id[64];
  char label[28];
  off_t size;

  struct sockaddr_in bcast_addr;
  struct sockaddr_in root_addr;
  struct sockaddr_in part_addr;

  time_t last_resolve;
  time_t last_reply;

  struct disk *disk;

  u_int outstanding_io_count;
  struct outstanding_io_head outstanding_io_list;
  u_int pending_io_count;
  struct outstanding_io_head pending_io_list;

  SLIST_ENTRY(device_state) entries;
};

SLIST_HEAD(device_state_head, device_state);

// most significant bit.
#define MSB(n) ((n) & ~((n) - 1))

// return the power of two greater than or equal to n.
#define POWER_OF_2(n) ({ \
  int __power = 1; \
  while ((1 << __power) < n) \
    __power++; \
  __power; \
})

// how long do we wait for a resolve response?
#define RESOLVE_TIMEOUT_MS (1000)

// drives can be configured to turn off when idle on the SC101 (but seemingly
// not on the SC101T?) in which case they can take up to 30s to spin up,
// so we back off on the retries.
#define SPINUP_INTERVAL_MS (10*1000)

// UDP allows for 64k packets, subtract 512b for request header and truncating
// to the next lowest power of 2 means the devices can probably support 32k
// I/Os, but we can choose a lower limit in case of packet loss.
// jumbo frames are not supported, so UDP packets >1500 bytes are split into
// multiple ethernet frames.
#define MAX_IO_SIZE (16*1024)

// maximum number of IOs to send to a particular device before queueing the
// request.
#define MAX_OUTSTANDING_IO (8)

#define SECTOR_SIZE (512)

#endif // __SC101_H__
